/*===============================================================
*   Copyright (C) 2022 All rights reserved.
*   
*   文件名称：server_func.cpp
*   创 建 者：QiuCC
*   创建日期：2022年10月09日
*   描    述：
*
*   更新日志：
*
================================================================*/
#include <iostream>
#include <stdio.h>
#include <netinet/in.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <cstring>
#include <string>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <jpeglib.h>
#include <linux/fb.h>
#include <typeinfo>
#include <linux/videodev2.h>

#include "server_func.h"

using namespace std;

int fd_fb;                                                    
static struct fb_var_screeninfo var; /* LCD可变参数 */
static unsigned int *fb_base = NULL; /* Framebuffer映射基地址 */                 
int lcd_w = 800 ,lcd_h= 480; //定义显示器分辨率


int server_init()//服务器初始化
{
	//创建服务器套接字
	int sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if(sockfd < 0)
	{
		perror("创建客户端套接字");
		exit(-1);
	}
	
	//绑定服务器ip地址和端口号
	memset(&server_addr, 0, server_size);
	memset(&client_addr, 0, client_size);
	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(PORT);
	server_addr.sin_addr.s_addr = inet_addr("0");

	//端口复用函数
	int tmp = 1;
	int ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp));
	if(ret < 0)
	{
		perror("端口复用");
		exit(-1);
	}
	
	
	//绑定套接字
	ret = bind(sockfd, (struct sockaddr *)&server_addr, server_size);
	if(ret < 0)
	{
		perror("绑定套接字");
		exit(-1);
	}
	
	//监听客户端链接
	ret = listen(sockfd, 10);
	if(ret < 0)
	{
		perror("监听客户端");
		exit(-1);
	}
	
	cout << "服务器正在监听客户端连接" << endl;
	
	return sockfd;
}

int connect(int sockfd)//客户端连接
{
	int confd = accept(sockfd, (struct sockaddr *)&client_addr, (socklen_t *)&client_size);
	if(confd == -1){
		perror("获取通信套接字");
		exit(-1);
	}
	cout << "IP地址为：" << inet_ntoa(client_addr.sin_addr) <<"的客户端建立通信成功，正在等待数据传输" << endl;
	
	return confd;
}

int video_init(int video_fd, char *(&mptr)[4], int (&size)[4])//摄像头初始化以及内核申请
{
	//1、获取摄像头的采集格式
	struct v4l2_fmtdesc vfmt1;
    vfmt1.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;//摄像头采集

    int i = 0;
    while(1)         //使用while循环从0开始测试index索引值
    {
        vfmt1.index = i;
        i++;
        int ret = ioctl(video_fd,VIDIOC_ENUM_FMT,&vfmt1);
        if (ret < 0)
        {
            perror("获取失败");
            break;
        }
        cout << "格式序号为：" << vfmt1.index << endl;
        cout << "是否为压缩格式" << vfmt1.flags << endl;
        cout << "格式名称：" << vfmt1.description << endl;
        cout << "所支持的格式：" << vfmt1.pixelformat << endl;
        cout << "保留字" << vfmt1.reserved[0] << endl;
    }

    //2、获取摄像头支持格式 ioctl(文件描述符,命令，与命令对应的结构体)
	struct v4l2_format vfmt2;

    vfmt2.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; //摄像头采集
    vfmt2.fmt.pix.width = 640; //设置摄像头采集参数，不可以任意设置
    vfmt2.fmt.pix.height = 480;
    vfmt2.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG; //设置视频采集格式 ，根据上一步测得，注意格式有yyuv和yuyv不要搞混
    
    int ret = ioctl(video_fd,VIDIOC_S_FMT,&vfmt2); //设置格式命令
    if (ret < 0)
    {
        perror("设置摄像头格式失败1");
    }

/*     memset(&vfmt2,0,sizeof(vfmt2));
    vfmt2.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    ret = ioctl(fd,VIDIOC_G_FMT,&vfmt2);
    if (ret < 0)
    {
        perror("设置格式失败2");
    } */

    if(vfmt2.fmt.pix.width == 640 && vfmt2.fmt.pix.height == 480 && vfmt2.fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG)
    {
        cout << "设置摄像头格式成功！" << endl;
    }else
    {
        cout << "设置摄像头格式失败2" << endl;
    }
	
	//3、内核空间申请
	struct v4l2_requestbuffers reqbuffer;
    reqbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    reqbuffer.count = 4; //申请4个缓冲区
    reqbuffer.memory = V4L2_MEMORY_MMAP;  //映射方式

    ret = ioctl(video_fd,VIDIOC_REQBUFS,&reqbuffer);
    if (ret < 0)
    {
        perror("申请空间失败");
    }
	else
	{
		cout << "空间申请成功" << endl;
	}

	//4、内存空间映射

    struct v4l2_buffer mapbuffer;
    //初始化type和index
    mapbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

    for(int i = 0; i <4;i++) {  //使用for对四个申请的空间进行轮询
        mapbuffer.index = i;
        ret = ioctl(video_fd,VIDIOC_QUERYBUF,&mapbuffer); //从内核空间中查询一个空间作映射
        if (ret < 0)
        {
            perror("查询内核空间失败");
        }
        //映射到用户空间
        mptr[i] = (char *)mmap(NULL,mapbuffer.length,PROT_READ|PROT_WRITE,MAP_SHARED,video_fd,mapbuffer.m.offset);
		size[i] = mapbuffer.length; 
		
        //查询后通知内核已经放回
        ret = ioctl(video_fd,VIDIOC_QBUF,&mapbuffer); 
        if (ret < 0)
        {
            perror("放回失败");
        }
    }
	cout << "内存映射成功" << endl;
	
	return 0;
}

int Data_acq(int video_fd, string filename, char *(&mptr)[4])//数据采集和发送
{
 	Pack_msg *package;
	package->func = 1;
	package->len = 0;
	package->msg = NULL;
	
	//开始采集
	int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	int ret = ioctl(video_fd,VIDIOC_STREAMON,&type); 
	if (ret < 0)
		{
			perror("开启失败");
		}
	
	while(1)
	{		
		//从队列中提取一帧数据
		struct v4l2_buffer readbuffer;
		readbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; //每个结构体都需要设置type为这个参赛要记住
		ret = ioctl(video_fd,VIDIOC_DQBUF,&readbuffer); 
		if (ret < 0)
			{
				perror("读取数据失败");
			}
		
/* 		FILE *file=fopen(filename.data(), "w+");  //打开一个文件
		fwrite( mptr[readbuffer.index],readbuffer.length,1,file);//写入文件
		fclose(file);    //写入完成，关闭文件 */
		
		int file_fd = open(filename.data(), O_RDWR | O_CREAT, 0664);
		//write(file_fd, mptr[readbuffer.index], readbuffer.length);
		package->msg = mptr[readbuffer.index];
		package->len = readbuffer.length;
	
		cout << "数据包功能号" << package->func << endl;
		cout << "数据包长度" << package->len << endl;
		cout << "数据包内容" << package->msg << endl;
		cout << "用户数据空间内容：" << mptr[readbuffer.index] << endl;
		cout << "用户数据空间内容大小" << readbuffer.length << endl;
		
		close(file_fd);
		sleep(3);	

	    ret = ioctl(video_fd, VIDIOC_QBUF, &readbuffer);
	    if(ret < 0)
		{
			perror("放回队列失败");
		}
	
		usleep(100);
		break;
	}
	//停止采集
	ret = ioctl(video_fd,VIDIOC_STREAMOFF,&type);
	return 0;
}

void release_video_init(char *(&mptr)[4], int (&size)[4])//释放内存
{
	//释放映射
	int i = 0;
    for(i = 0; i < 4; i++)
	{
		cout << "内存正在释放" << endl;
		munmap(mptr[i], size[i]);
	}
	
	cout << "内存释放完毕" << endl;
	return;
}

int send_video()//发送数据包
{
	//1、打开摄像头设备文件
    int fd1 = open("/dev/video0",O_RDWR);
    if (fd1 < 0)
    {
        perror("打开设备失败");
        return -1;
    }
	char *mptr[4];//保存映射后用户空间的首地址
	int size[4];//保存映射长度用于后期释放
	
/* 	int fd2 = open("/dev/video2",O_RDWR);
    if (fd2 < 0)
    {
        perror("打开设备失败");
        return -1;
    } */
	
	video_init(fd1, mptr, size);//内核空间申请
	cout << size << endl;
	
	Data_acq(fd1, "video1.jpg", mptr);//摄像头数据采集
	cout << size << endl;
	
	release_video_init(mptr, size);//释放内存
	cout << size << endl;
	

	//video_init(fd2, "2.jpg");

    close(fd1);
/* 	close(fd2); */
	cout << "数据发送结束" << endl;
	
    return 0;
}


























































































